Сферические факториалы
TL;DR
Lang avg ns of 1M calls
------- ------------------
Rust 0
Java 3
PyPy 8
Python 537
K 756
O-CPS 7702
Erlang 10699/1806/436/9
LuaJIT 33856
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
Джава как эталон считающий факториал пятерочки за 4нс, которые уходят на разогрев ВМ.
public class fac {
public static void main(String[] args) {
long a = System.nanoTime();
long i = 10000000;
while (i > 0) {
calc(5);
i--;
}
long b = System.nanoTime();
System.out.println("Fac: " + (b-a)/10000000 + "ns");
}
public static long calc(long n) {
if (n == 1)
return 1;
else
return n * calc(n - 1);
}
}
$ javac fac.java
$ java -cp . fac
Fac: 3ns
rustc 1.14.0-nightly (cae6ab1c4 2016-11-05)
На расте вообще zero-cost факториал получился :-) Даже factoria 20 не выходит из 0 наносекунд. Так и должно быть!
#[bench]
fn fac_rust(b: &mut Bencher) {
let mut x: i64 = 0;
b.iter(|| { x = factorial(5); });
}
fn factorial(value: i64) -> i64 { if value==1 { 1 } else { return value *
factorial(value-1); } }
test fac_rust ... bench: 0 ns/iter (+/- 0)
0ns
Теперь переходим к CPS интерпретаторам:
KDB+ 3.4 2016.10.10 Copyright (C) 1993-2016 Kx Systems
q)f:{$[x=1;1;x*.z.s x-1]}
q)\t f'[1000000#5]
756
756ns
LuaJIT 2.0.4 -- Copyright (C) 2005-2015 Mike Pall. http://luajit.org/
function factorial(n)
if n == 1 then return 1
else return n * factorial(n - 1) end end
local x = os.clock()
local f = 0
for i=0,1000000,1 do f = factorial(5) end
print(f,(os.clock()-x)*1000000)
$ luajit fac.lua
120 33856
33us
Erlang/OTP 19 [erts-8.1] [64-bit] [smp:8:8]
> Fac = fun Factorial(X) -> case X of 1 -> 1; _ -> X * Factorial(X-1) end end.
#Fun
> lists:sum([element(1,timer:tc(fun() -> Fac(5)
end))||_<-lists:seq(1,1000000)])/1000000.
10.699388
10us
fac2(X) -> case X of 1 -> 1; _ -> X * fac2(X-1) end.
fac(1,N) -> N;
fac(I,N) -> fac(I-1,N*I).
fac(N) -> fac(N-1,N).
test(F) ->
A = list_to_integer(os:getenv("FAC")),
lists:sum([element(1,timer:tc(fun() -> fac(A)
end))||_<-lists:seq(1,1000000)])/1000000.
$ FAC=5 erl
> c(fac), l(fac).
> fac:test().
0.009811
9ns
test(F) ->
A = list_to_integer(os:getenv("FAC")),
lists:sum([element(1,timer:tc(fun() -> fac2(A)
end))||_<-lists:seq(1,1000000)])/1000000.
> fac:test().
0.436004
436ns
test() ->
F = fun Fac(1,N) -> N;
Fac(I,N) -> Fac(I-1,N*I) end,
A = list_to_integer(os:getenv("FAC")),
lists:sum([element(1,timer:tc(fun() -> F(A-1,A)
end))||_<-lists:seq(1,1000000)])/1000000.
1.806826
1806ns
Python 2.7.12
#!/usr/bin/python
import timeit
def fac(x):
if x == 1:
return 1
else:
return x * fac(x - 1)
n = 1000000
r = timeit.timeit(lambda: fac(5), number=n)
print("{0:.6f}ns".format(r*1000))
542.725086ns
PyPy 5.1.2 with GCC 5.3.1 20160413
#!/usr/bin/env pypy
import timeit
def fac(x):
if x == 1:
return 1
else:
return x * fac(x - 1)
n = 1000000
r = timeit.timeit(lambda: fac(5), number=n)
print("{0:.6f}ns".format(r*1000))
8.576870ns
Welcome to O-CPS Interpreter v0.11.0!
$ cargo build ; rlwrap ./target/debug/console 2>/dev/null
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Welcome to O-CPS Interpreter v0.11.0!
> fac:{$[x=1;1;x*fac[x-1]]};fac 5
120
#[bench]
fn fac(b: &mut Bencher) {
let mut i = Interpreter::new().unwrap();
let ref mut code = ast::parse(&"fac:{$[x=1;1;x*fac[x-1]]}".to_string());
i.run(code).unwrap();
let ref mut f = ast::parse(&"fac[5]".to_string());
b.iter(|| {
i.run(f);
})
}
test fac ... bench: 7,702 ns/iter (+/- 376)
7us